// $Id: filemeshbit.cpp 72 2004-12-02 15:03:28Z jonas $
#include "filemeshbit.h"
#include "file.h"
#include <memory.h>
#include <strings.h>
#include <iostream>
#include <math.h>
using namespace std;

FileMeshBit::FileMeshBit()
{
	int size = (fileMeshBitCount-1)/8 + 1;

	// Reserve space for all bits
	m_bits = new unsigned char[size];

	// Unset all bits
	bzero(m_bits, size);
}

/**
 * Copy constructor. Make an identical copy of a FileMeshBit object.
 *
 * @param fileMesh The object to copy from.
 */
FileMeshBit::FileMeshBit(const FileMeshBit &fileMesh)
{
	int size = (fileMeshBitCount-1)/8 + 1;
	m_bits = new unsigned char[size];
	memcpy(m_bits, fileMesh.m_bits, size);
}

/**
 * Clean up allocated memory.
 */
FileMeshBit::~FileMeshBit()
{
	delete[] m_bits;
}

/**
 * Returns a pointer to a copy of the input. This is equal to calling new, and the caller MUST handle delete apropriately!
 * @return A new object identical to this object
 */
FileMesh *FileMeshBit::newCopy()
{
	//return new FileMeshBit(*((FileMeshBit *)fileMesh));
	return new FileMeshBit(*this);
}

/**
 * Estimates a distance from the hash using a stored file mesh.
 *
 * @param hash The hash to find out the distance to.
 * @returns The estimated distance.
 */
unsigned int FileMeshBit::calcDistance(unsigned int hash)
{
	int i=0;
	int index = getIndex(hash);

	// Calc how large keyspace each bit in the filter represents
	int fileMeshBitWidth = RAND_MAX/fileMeshBitCount;

	// Find nearest set bit
	while(index+i<fileMeshBitCount || index-i>=0){
		if((index+i<fileMeshBitCount && getBit(index+i)) || (index-i>=0 && getBit(index-i))){
// cout << "Bloom says:\t" << (i*bloomFilterBitWidth) << "\t" << i << endl;	// DEBUG
			return i*fileMeshBitWidth;
		}
		i++;
	}
	return RAND_MAX;
}

/**
 * Update the filter by setting the apropriate bit for the supplied hash.
 *
 * @param hash The hash to insert into the filter
 */
void FileMeshBit::update(unsigned int hash, map<File *, StoredFile *> &fileStore)
{
	setBit(getIndex(hash), true);
}

/**
 * Since we cannot update the filter correctly if a hash is removed from it we
 * need to rebuild it if it happens. This method inserts all hashes from the
 * supplied file store into the filter.
 *
 * @param fileStore The file store to insert into the filter.
 */
void FileMeshBit::rebuild(map<File *, StoredFile *> &fileStore)
{
	// Reset all bits to zero
	bzero(m_bits, (fileMeshBitCount-1)/8 + 1);

	// Map all stored files to the filter
	for (map<File *, StoredFile *>::iterator file=fileStore.begin(); file!=fileStore.end(); file++) {
		setBit((int) getIndex((*file).first->m_hash), true);
	}
}

/**
 * @todo Document this...
 *
 * @param routingTable
 */
/*void FileMeshBit::rebuild(std::list<RoutingEntry> &routingTable)
{
	// Reset all bits to zero
	bzero(bits, (bloomFilterBitCount-1)/8 + 1);

	// Map all stored files to the filter
	for (list<RoutingEntry>::iterator entry=routingTable.begin(); entry!=routingTable.end(); entry++) {
		setBit((int) getIndex((*entry).m_hash), true);
	}
}*/

/**
 * Get the state of a bit in the filter.
 *
 * @param index The index of the bit to retrieve status on.
 * @returns True if the bit is set, false otherwise.
 */
bool FileMeshBit::getBit(int index)
{
	assert(index>=0 && index<fileMeshBitCount);

	// Mask out and return bit value
	return (m_bits[index/8] & (unsigned char) ::pow(2, 7-(index%8))) > 0;
}

/**
 * Set a bit in the filter to a specified value.
 *
 * @param index The index of the bit to manipulate.
 * @param value The value to set the bit to. True is set, false is unset.
 */
void FileMeshBit::setBit(int index, bool value)
{
	assert(index>=0 && index<fileMeshBitCount);

	// Do we need to change a bit?
	if (getBit(index) != value) {
		if (value) {
			m_bits[index/8] |= (unsigned char) ::pow(2, 7-(index%8));
		} else {
			m_bits[index/8] &= (unsigned char) (255 - ::pow(2, 7-(index%8)));
		}
	}
}

/**
 * Get the index position in the filter for a specified value
 *
 * @param value The value that should be used to get the index
 * @return index The index of the value
 */
int FileMeshBit::getIndex(int value)
{
	return (int) ((value/(double)RAND_MAX)*fileMeshBitCount);
}
